/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2005, 2008. All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:  Use,
 * duplication or disclosure restricted by GSA ADP Schedule
 * Contract with IBM Corp.
 *******************************************************************************/

package org.eclipsecon.dui.interpreter.internal;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jface.resource.ColorDescriptor;
import org.eclipse.jface.resource.FontDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Text;
import org.eclipsecon.dui.interpreter.IContextAware;
import org.eclipsecon.dui.swtmodel.Constant;
import org.eclipsecon.dui.swtmodel.MActionHandler;
import org.eclipsecon.dui.swtmodel.MButton;
import org.eclipsecon.dui.swtmodel.MColor;
import org.eclipsecon.dui.swtmodel.MCombo;
import org.eclipsecon.dui.swtmodel.MComposite;
import org.eclipsecon.dui.swtmodel.MControl;
import org.eclipsecon.dui.swtmodel.MFont;
import org.eclipsecon.dui.swtmodel.MGridData;
import org.eclipsecon.dui.swtmodel.MGridLayout;
import org.eclipsecon.dui.swtmodel.MLabel;
import org.eclipsecon.dui.swtmodel.MModel;
import org.eclipsecon.dui.swtmodel.MSWT;
import org.eclipsecon.dui.swtmodel.MText;
import org.eclipsecon.dui.swtmodel.Parameter;
import org.eclipsecon.dui.swtmodel.Template;
import org.eclipsecon.dui.swtmodel.TemplateUsage;
import org.eclipsecon.dui.swtmodel.Variable;
import org.eclipsecon.dui.swtmodel.util.SwtmodelSwitch;
import org.osgi.framework.Bundle;

public class SWTModelSwitch extends SwtmodelSwitch {
	private final Composite fParent;
	private LocalResourceManager fResources;
	private FontData fDefaultFontData;
	private final ModelContext fContext;
	private TemplateUsage fTemplate;

	public SWTModelSwitch(Composite parent, ModelContext context) {
		fParent= parent;
		fContext= context;
		fDefaultFontData= fParent.getDisplay().getSystemFont().getFontData()[0];
		fResources= new LocalResourceManager(JFaceResources.getResources(), parent);
	}

	private SWTModelSwitch(Composite parent, ModelContext context, TemplateUsage template) {
		this(parent, context);
		fTemplate= template;
	}

	@Override
	public Object caseMModel(MModel object) {
		return doSwitch(object.getRootComposite());
	}

	/*
	 * @see
	 * org.eclipsecon.dui.model.util.ModelSwitch#caseMComposite(org.eclipsecon
	 * .dui.model.MComposite)
	 */
	@Override
	public Composite caseMComposite(MComposite object) {
		Composite composite= new Composite(fParent, getStyle(object));
		apply(object, composite);

		SWTModelSwitch modelSwitch= new SWTModelSwitch(composite, fContext);
		EList<MControl> children= object.getChildren();
		for (MControl child : children) {
			modelSwitch.doSwitch(child);
		}

		return composite;
	}

	/*
	 * @see
	 * org.eclipsecon.dui.model.util.ModelSwitch#caseMColor(org.eclipsecon.dui
	 * .model.MColor)
	 */
	@Override
	public ColorDescriptor caseMColor(MColor object) {
		return ColorDescriptor.createFrom(new RGB(object.getRed(), object.getBlue(), object.getGreen()));
	}

	/**
	 * @param object
	 * @return
	 */
	@Override
	public FontDescriptor caseMFont(MFont object) {
		String name= object.getName();
		int height= object.getHeight();

		if (name == null)
			name= fDefaultFontData.getName();

		if (height == -1)
			height= fDefaultFontData.getHeight();

		return FontDescriptor.createFrom(name, height, getStyle(object.getStyle()));
	}

	/*
	 * @see
	 * org.eclipsecon.dui.model.util.ModelSwitch#caseMButton(org.eclipsecon.
	 * dui.model.MButton)
	 */
	@Override
	public Object caseMButton(MButton object) {
		Button button= new Button(fParent, getStyle(object));
		button.setText((String) doSwitch(object.getName()));
		apply(object, button);

		if (object.getAction() != null) {
			SelectionListener handler= (SelectionListener) doSwitch(object.getAction());
			button.addSelectionListener(handler);
		}
		return button;
	}

	/*
	 * @see
	 * org.eclipsecon.dui.swtmodel.util.SwtmodelSwitch#caseMCombo(org.eclipsecon
	 * .dui.swtmodel.MCombo)
	 */
	@Override
	public Object caseMCombo(MCombo object) {
		Combo combo= new Combo(fParent, getStyle(object));

		for (Parameter param : object.getValues()) {
			combo.add((String) doSwitch(param));
		}

		apply(object, combo);

		if (object.getAction() != null) {
			SelectionListener handler= (SelectionListener) doSwitch(object.getAction());
			combo.addSelectionListener(handler);
		}
		return combo;
	}

	@Override
	public Object caseMActionHandler(MActionHandler object) {
		Bundle contributingBundle= Platform.getBundle(object.getBundleName());
		if (contributingBundle == null)
			throw new IllegalArgumentException("Bundle not found: " + object.getBundleName());

		// load the requested class from this bundle
		Class<SelectionListener> classInstance= null;
		try {
			classInstance= contributingBundle.loadClass(object.getClass_());
		} catch (Exception e1) {
			throw new IllegalArgumentException("Class not found: " + object.getClass(), e1);
		}

		// create a new instance
		SelectionListener result= null;
		try {
			result= classInstance.newInstance();
			if (result instanceof IContextAware) {
				IContextAware contextAware= (IContextAware) result;
				contextAware.setContext(fContext);

			}
		} catch (Exception e) {
			throw new IllegalArgumentException("Class cannot be instantiated or is not a SelectionListener: " + object.getClass(), e);
		}
		return result;
	}

	@Override
	public Object caseTemplateUsage(TemplateUsage object) {
		Map<String, String> parameterMapping= new HashMap<String, String>();
		int i= 0;
		for (String s : object.getTemplate().getVariables()) {
			parameterMapping.put(s, object.getParameters().get(i));
			i++;
		}

		TreeIterator<EObject> allContents= object.getTemplate().eAllContents();
		for (Iterator<EObject> iterator= allContents; iterator.hasNext();) {
			EObject o= iterator.next();
			if (o instanceof Variable) {
				Variable var= (Variable) o;
				String value= var.getValue();
				var.setActualValue(parameterMapping.get(value));
			}

		}

		SWTModelSwitch ms= new SWTModelSwitch(fParent, fContext, object);
		for (MControl control : object.getTemplate().getWidgets()) {
			ms.doSwitch(control);
		}

		return null;
	}

	/*
	 * @see
	 * org.eclipsecon.dui.model.util.ModelSwitch#caseMLabel(org.eclipsecon.dui
	 * .model.MLabel)
	 */
	@Override
	public Object caseMLabel(MLabel object) {
		Label label= new Label(fParent, getStyle(object));
		label.setText((String) doSwitch(object.getName()));
		apply(object, label);

		return label;
	}

	/*
	 * @see
	 * org.eclipsecon.dui.model.util.ModelSwitch#caseMText(org.eclipsecon.dui
	 * .model.MText)
	 */
	@Override
	public Object caseMText(MText object) {
		Text input= new Text(fParent, getStyle(object));
		if (object.getValue() != null)
			input.setText((String) doSwitch(object.getValue()));
		apply(object, input);

		return input;
	}

	@Override
	public Object caseConstant(Constant object) {
		return object.getValue();
	}

	@Override
	public Object caseVariable(Variable object) {
		return object.getActualValue();
	}

	/*
	 * @see
	 * org.eclipsecon.dui.model.util.ModelSwitch#caseMGridLayout(org.eclipsecon
	 * .dui.model.MGridLayout)
	 */
	@Override
	public Object caseMGridLayout(MGridLayout object) {
		GridLayout layout= new GridLayout();
		layout.marginWidth= object.getMarginWidth();
		layout.marginHeight= object.getMarginHeight();
		layout.makeColumnsEqualWidth= object.isMakeColumnsEqualWidth();
		layout.numColumns= object.getNumColumns();

		return layout;
	}

	/*
	 * @see
	 * org.eclipsecon.dui.model.util.ModelSwitch#caseMGridData(org.eclipsecon
	 * .dui.model.MGridData)
	 */
	@Override
	public Object caseMGridData(MGridData object) {
		GridData data= new GridData();
		data.horizontalAlignment= object.getHorizontalAlignment().getValue();
		data.verticalAlignment= object.getVerticalAlignment().getValue();
		data.grabExcessHorizontalSpace= object.isGrabExcessHorizontalSpace();
		data.grabExcessVerticalSpace= object.isGrabExcessVerticalSpace();
		data.horizontalSpan= object.getHorizontalSpan();
		data.verticalSpan= object.getVerticalSpan();
		data.widthHint= object.getWidthHint();
		data.heightHint= object.getHeightHint();

		return data;
	}

	private void apply(MControl mControl, Control control) {
		applyColor(mControl, control);
		applyFont(mControl, control);
		applyLayoutData(mControl, control);
		applyAdaper(mControl, control);
	}

	private void applyAdaper(MControl mControl, Control control) {
		if (!(mControl.eContainer() instanceof Template)) {
			mControl.eAdapters().add(new SWTControlAdapter(control));
		} else {
			SWTControlAdapter adapter= (SWTControlAdapter) EcoreUtil.getAdapter(mControl.eAdapters(), SWTControlAdapter.class);
			if (adapter == null) {
				adapter= new SWTControlAdapter();
				mControl.eAdapters().add(adapter);
			}
			adapter.setControl(fTemplate, control);
		}
	}

	private void apply(MComposite mComposite, Composite composite) {
		apply((MControl) mComposite, (Control) composite);
		applyLayout(mComposite, composite);
	}

	private void applyColor(MControl mControl, Control control) {
		if (mControl.getBackground() != null) {
			ColorDescriptor fgColor= (ColorDescriptor) doSwitch(mControl.getBackground());
			control.setBackground(fResources.createColor(fgColor));
		}

		if (mControl.getForeground() != null) {
			ColorDescriptor fgColor= (ColorDescriptor) doSwitch(mControl.getForeground());
			control.setForeground(fResources.createColor(fgColor));
		}
	}

	private void applyFont(MControl mControl, Control control) {
		if (mControl.getFont() != null) {
			FontDescriptor font= (FontDescriptor) doSwitch(mControl.getFont());
			control.setFont(fResources.createFont(font));
		}
	}

	private void applyLayoutData(MControl mControl, Control control) {
		if (mControl.getLayoutdata() != null) {
			Object data= doSwitch(mControl.getLayoutdata());
			control.setLayoutData(data);
		}
	}

	private void applyLayout(MComposite mComposite, Composite composite) {
		if (mComposite.getLayout() != null) {
			Layout layout= (Layout) doSwitch(mComposite.getLayout());
			composite.setLayout(layout);
		}
	}

	private int getStyle(MControl control) {
		return getStyle(control.getStyle());
	}

	private int getStyle(EList<MSWT> styles) {
		int styleVal= SWT.None;

		for (MSWT style : styles) {
			styleVal|= style.getValue();
		}

		return styleVal;
	}
}